package vip.xiaomaoxiaoke.functiondemo.msg.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import vip.xiaomaoxiaoke.functiondemo.msg.util.SerializeUtil;
import vip.xiaomaoxiaoke.functiondemo.msg.interceptor.LocalMessageAnnotationInterceptor;
import vip.xiaomaoxiaoke.functiondemo.msg.message.Message;
import vip.xiaomaoxiaoke.functiondemo.msg.message.impl.NativeMethodMessage;
import vip.xiaomaoxiaoke.functiondemo.msg.repository.LocalMessageRepository;
import vip.xiaomaoxiaoke.functiondemo.msg.repository.LocalMessageTable;
import vip.xiaomaoxiaoke.functiondemo.msg.strategies.RefusalStrategies;

import java.lang.reflect.InvocationTargetException;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

/**
 * 可靠方法执行服务
 * <p>
 * 该类负责管理本地消息的持久化、重试和执行逻辑。
 */
@Slf4j
public class ReliableMethodExecuteService {

    /**
     * 本地消息数据源
     */
    private final LocalMessageRepository localMessageRepository;

    /**
     * 拒绝策略
     */
    private final RefusalStrategies refusalStrategies;

    public ReliableMethodExecuteService(LocalMessageRepository localMessageRepository, RefusalStrategies refusalStrategies) {
        this.localMessageRepository = Objects.requireNonNull(localMessageRepository, "localMessageRepository cannot be null");
        this.refusalStrategies = Objects.requireNonNull(refusalStrategies, "refusalStrategies cannot be null");
    }

    /**
     * 加载并重新发送未成功的消息
     * <p>
     * 建议：分布式环境使用 XXL-Job 执行，单机环境使用本地定时任务执行。
     *
     * @param sizePreTask 每次加载的消息数量
     */
    public void loadAndResend(int sizePreTask) {
        Long latestId = 0L;
        List<LocalMessageTable> localMessageTables = this.localMessageRepository.loadNotSuccessByUpdateGt(latestId, sizePreTask);

        while (CollectionUtils.isNotEmpty(localMessageTables)) {
            log.info("Loaded {} messages for retry", localMessageTables.size());
            retrySend(localMessageTables);

            // 更新最新 ID
            latestId = localMessageTables.stream()
                    .map(LocalMessageTable::getId)
                    .max(Comparator.naturalOrder())
                    .orElse(Long.MAX_VALUE);

            // 继续加载下一批消息
            localMessageTables = this.localMessageRepository.loadNotSuccessByUpdateGt(latestId, sizePreTask);
        }
    }

    /**
     * 重新发送消息
     *
     * @param localMessageTables 待重试的消息列表
     */
    private void retrySend(List<LocalMessageTable> localMessageTables) {
        localMessageTables.forEach(localMessageTable -> {
            try {
                run(localMessageTable);
            } catch (Exception e) {
                log.error("Error occurred while retrying message with ID: {}", localMessageTable.getId(), e);
            }
        });
    }

    /**
     * 保存并发送消息
     *
     * @param message 消息对象
     */
    public void saveAndSend(Message message) {
        if (message == null) {
            throw new IllegalArgumentException("Message cannot be null");
        }

        LocalMessageTable localMessageTable = message.convert();
        if (localMessageTable == null) {
            throw new IllegalArgumentException("Failed to convert message to LocalMessageTable");
        }

        localMessageTable.init();
        saveToDB(localMessageTable);

        try {
            addCallbackOrRunTask(localMessageTable);
        } catch (Exception e) {
            log.error("Error occurred while adding callback or running task for message ID: {}", localMessageTable.getId(), e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 添加事务回调或直接运行任务
     *
     * @param localMessageTable 消息表对象
     * @throws InvocationTargetException 如果方法调用失败
     * @throws IllegalAccessException    如果无法访问方法
     */
    private void addCallbackOrRunTask(LocalMessageTable localMessageTable) throws InvocationTargetException, IllegalAccessException {
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            // 添加监听器，在事务提交后触发后续任务
            TransactionSynchronization transactionSynchronization = new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    try {
                        run(localMessageTable);
                    } catch (Exception e) {
                        log.error("Error occurred in afterCommit for message ID: {}", localMessageTable.getId(), e);
                        throw new RuntimeException(e);
                    }
                }
            };
            TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);
        } else {
            run(localMessageTable);
        }
    }

    /**
     * 执行消息对应的方法
     *
     * @param localMessageTable 消息表对象
     * @throws InvocationTargetException 如果方法调用失败
     * @throws IllegalAccessException    如果无法访问方法
     */
    public void run(LocalMessageTable localMessageTable) throws InvocationTargetException, IllegalAccessException {
        if (localMessageTable == null) {
            throw new IllegalArgumentException("LocalMessageTable cannot be null");
        }

        // 判断是否需要执行拒绝策略
        if (localMessageTable.getRetryCount() >= localMessageTable.getMaxRetries()) {
            try {
                refusalStrategies.execute(localMessageTable);
                localMessageTable.setRefusalStrategiesState(1);
                localMessageRepository.update(localMessageTable);
                log.info("Refusal strategy executed successfully for message ID: {}", localMessageTable.getId());
            } catch (Exception e) {
                localMessageTable.setRefusalStrategiesState(-1);
                localMessageRepository.update(localMessageTable);
                log.error("Error occurred while executing refusal strategy for message ID: {}", localMessageTable.getId(), e);
            }
            return;
        }

        NativeMethodMessage convert = new NativeMethodMessage().convert(localMessageTable);
        LocalMessageAnnotationInterceptor.Mk mk = LocalMessageAnnotationInterceptor.map.get(convert.getMethodKey());
        if (mk == null || mk.getBean() == null || mk.getMethod() == null) {
            log.error("Failed to find method or bean for message ID: {}", localMessageTable.getId());
            return;
        }

        try {
            Object invoke = mk.getMethod().invoke(mk.getBean(), SerializeUtil.deserialize(convert.getParamValueList(), mk.getMethod()));
            // 没有异常则执行成功，设置执行状态为成功
            localMessageTable.setState(1);
            localMessageTable.setResponseContent(JSON.toJSONString(invoke));
            localMessageRepository.update(localMessageTable);
            log.info("Message executed successfully with response: {}", JSON.toJSONString(invoke));
        } catch (Exception e) {
            // 执行发生异常
            log.error("Error occurred while executing message ID: {}", localMessageTable.getId(), e);
            localMessageTable.setResponseContent(e.getMessage());
            localMessageTable.setState(-1);
            localMessageTable.setRetryCount(localMessageTable.getRetryCount() + 1);
            localMessageRepository.update(localMessageTable);
        }
    }

    /**
     * 保存消息到数据库
     *
     * @param localMessage 消息表对象
     */
    private void saveToDB(LocalMessageTable localMessage) {
        if (localMessage == null) {
            throw new IllegalArgumentException("LocalMessageTable cannot be null");
        }

        try {
            this.localMessageRepository.save(localMessage);
            log.info("Message saved successfully with ID: {}", localMessage.getId());
        } catch (Exception e) {
            log.error("Error occurred while saving message to DB: {}", localMessage.getId(), e);
            throw new RuntimeException(e);
        }
    }
}
